/*
 * @Description:
 * @version: 1.0.0
 * @Author: YinJianFeng
 * @Date: 2024-03-29 10:49:34
 */
import {
  AllowedCadTreeNodeType,
  AntDesignTheme,
  cadAppLog,
  CmdPreChecker,
  CMEVENTS,
  DocumentEvents,
  FeatureManager,
  generateDocUniqueKey,
  IAssemblyDocTreeNode,
  IEnvironment,
  MessageTip,
  Playground,
} from '@amcax/base';
import { PickerMgr, RendererMgr, Snaper } from '@amcax/renderer';
import {
  CAM_EVENT,
  COMMON_EVENTS,
  LOADING_EVENTS,
  SHAPE_CHANGE_PENDING_EVENT_HANDLED,
} from '@app-cad/common/Constants';
import { DocumentContext } from '@app-cad/common/providers/documentProvider';
import { EnvironmentContext } from '@app-cad/common/providers/environmentProvider';
import { CadEnvironments } from '@app-cad/environments';
import { referenceRenderManager } from '@app-cad/manager/ReferenceRenderManager';
import {
  application,
  coreEngine,
  ctx2,
  environmentManager,
  referenceManager,
  unitManager,
  userEvents,
} from '@app-cad/module';
import {
  DocInitFunc,
  initPart,
  initProduct,
  visualDataErrorMessage,
} from '@app-cad/pages/Index-funcs';
import { loadCmdPreCheckers } from '@app-cad/pages/pre-checker';
import { ConfigProvider } from 'antd';
import { useEffect, useRef, useState } from 'react';

import { UserSettingProvider } from '@app-base/common/providers/userSettingProvider';
import { CommandBar, CommandUpBar } from '@app-base/components/CommandBar';
import { measureRenderManager } from '@app-base/manager/MeasureRenderManager';
import { httpApi } from '@app-base/server/httpApi';
import { UnitSwitch } from '@app-cad/common/components/unit-switch';
import { ToastContainer } from '@app-cad/common/utils/MessageTip';
import { ErrorNotifier } from '@app-cad/components/ErrorNotifier';
import { useAsyncEffect } from 'ahooks';
import { debounce } from 'lodash';
import CommonReloadModal from '../common/components/CommonReloadModal';
import { sleep } from '../common/utils/sleep';
import { CSS2DObjectReactRenderer } from './CSS2DObjectReactRenderer';
import { MarkRenderer } from './MarkRenderer';

type CadSupportedType = AllowedCadTreeNodeType;

export default function Main() {
  const [showLoading, setShowLoading] = useState(true);
  const [loadingProgress, setLoadingProgress] = useState(-1);
  const [docTree, setDocTree] = useState<
    IAssemblyDocTreeNode & { documentInfo: CadDocument.DocumentInfo }
  >();
  const loadVisualDataFailed = useRef(false);
  const [environmentId, setEnvironmentId] = useState<CadEnvironments>();
  const [workingObjectDxid, setWorkingObjectDxid] = useState<string>();
  // 全局的长度和角度单位

  const [openReload, setOpenReload] = useState(false);

  const showConnectMessage = false;
  const [cmdPreCheckers, setCmdPreCheckers] = useState<CmdPreChecker[]>([]);

  const [inited, setInited] = useState(false);

  const [documentInfo, setDocumentInfo] = useState<CadDocument.DocumentInfo>();
  const documentInfoRef = useRef<CadDocument.DocumentInfo>();

  const [deltaHeight, setDeltaHeight] = useState(0);

  useEffect(() => {
    if (window.__MICRO_APP_ENVIRONMENT__) {
      // notice: this number should be updated according to the Base App bottom tab height change
      setDeltaHeight(70);
    } else {
      setDeltaHeight(0);
    }
  }, []);

  useAsyncEffect(async () => {
    const docInfo = (await httpApi.getDocumentInfoByUuid()).data;
    setDocumentInfo(docInfo);
    userEvents.emit(COMMON_EVENTS.DOCUMENT_INFO_UPDATED, docInfo);
    documentInfoRef.current = docInfo;
    startApp(docInfo);

    userEvents.on(DocumentEvents.WORKING_OBJECT_CHANGED, (val: string) => {
      setWorkingObjectDxid(val);
    });

    userEvents.on(CMEVENTS.ENV_CHANGED, (env: IEnvironment) => {
      setEnvironmentId(env.id as CadEnvironments);
    });

    const electronCenter = (window as any).electronCenter;
    electronCenter?.on(CMEVENTS.COMMANDBAR_CHANGED, (style: string) => {
      userEvents.emit(CMEVENTS.COMMANDBAR_CHANGED, style);
    });
  }, []);

  const updateThumbnail = debounce(
    () =>
      setTimeout(() => {
        const currentDocInfo = documentInfoRef.current;
        if (currentDocInfo?.collectionId && currentDocInfo?.id) {
          const imageBase64 = ctx2.viewer.getScreenshotImage();
          httpApi.updateThumbnail(
            currentDocInfo.collectionId,
            currentDocInfo.id,
            imageBase64,
          );
        }
      }, 400),
    1000,
    {
      leading: true,
      trailing: true,
    },
  );
  const startApp = async (docInfo: CadDocument.DocumentInfo) => {
    // 初始化单位配置
    try {
      const res = (await httpApi.getUnit()).data;
      unitManager.init(res);
    } catch (e) {
      console.warn('单位配置获取失败：', e);
    }

    try {
      await application.start({
        showAxes: false,
        init: async () => {
          await loadCmdPreCheckers(setCmdPreCheckers);

          userEvents.on(CMEVENTS.AUTH_FAILED, () => {
            window.location.replace('/login');
          });

          cadAppLog('startApp');

          ctx2.viewer.sceneSetup.setFitObjectViewBaseVector([0.4, -1, 0.6]);

          userEvents.on(LOADING_EVENTS.LOADING_CHANGE, (flag: boolean) => {
            setShowLoading(flag);
          });
          userEvents.on(LOADING_EVENTS.LOADING_PROGRESS, (progress: number) => {
            setLoadingProgress(progress);
          });

          initRenderer(docInfo.type);
          if (docInfo.type !== 'cam') {
            const initializers: Record<CadSupportedType, DocInitFunc> = {
              part: initPart,
              product: initProduct,
            };

            let retryCount = 0;
            const maxRetries = 3;

            const docTreeRoot = {
              key: generateDocUniqueKey(docInfo.uuid),
              collectionUUID: docInfo.collectionUuid,
              documentInfo: { ...docInfo },
              version: parseInt(docInfo.versionDetailResultVo.version, 10),
              title: docInfo.name,
              type: docInfo.type as AllowedCadTreeNodeType,
              visible: true,
            };
            while (retryCount < maxRetries) {
              try {
                await initializers[docInfo.type]({
                  docTreeRoot,
                  setShowLoading,
                  setLoadingProgress,
                  setDocTree,
                });
                break; // exit loop on success
              } catch (e: any) {
                if (e.message.includes(visualDataErrorMessage)) {
                  coreEngine.stop();

                  retryCount++;
                  if (retryCount >= maxRetries) {
                    setOpenReload(true);
                  }

                  loadVisualDataFailed.current = true;

                  await sleep(10e3); // hold 10 seconds before retry
                } else {
                  throw e; // rethrow the error for outer try-catch to handle
                }
              }
            }
          }
        },
        environments: async () => {
          const { nextCadEnvironment } = await import(
            '@app-cad/environments/nextcad'
          );
          const { productEnvironment } = await import(
            '@app-cad/environments/product'
          );

          const { nextMeshEnvironment } = await import(
            '@app-nextmesh-env/nextmesh'
          );
          const { nextCamEnvironment } = await import(
            '@app-cad/environments/nextcam'
          );
          return [
            nextCadEnvironment,
            productEnvironment,
            nextMeshEnvironment,
            nextCamEnvironment,
          ];
        },
        loaded: () => {
          if (loadVisualDataFailed.current) {
            return;
          }

          if (docInfo.type === 'part') {
            environmentManager.switchTo(CadEnvironments.NextCAD);
          } else if (docInfo.type === 'product') {
            environmentManager.switchTo(CadEnvironments.Product);
          } else if (docInfo.type === 'cam') {
            environmentManager.switchTo(CadEnvironments.NextCam);
          }
          const onCamModelLoaded = () => {
            setShowLoading(false);
          };
          ctx2.viewer.fitObjectView().then(() => updateThumbnail());
          userEvents
            .on(SHAPE_CHANGE_PENDING_EVENT_HANDLED, updateThumbnail)
            .on(CMEVENTS.MODEL_PLACEMENT_CHANGED, updateThumbnail)
            .on(CAM_EVENT.MODEL_LOADED, onCamModelLoaded);
          setInited(true);
        },
        featureFlagsUrl:
          (window.__MICRO_APP_ENVIRONMENT__
            ? '/app-cad/features.json'
            : '/features.json') +
          '?r=' +
          `${Math.random()}`.substring(2),
      });
    } catch (e) {
      console.error('加载失败：', e);
      MessageTip.error(e['response']?.data?.msg || '加载失败');
      setShowLoading(false);
    }
  };

  const initRenderer = (docType: string) => {
    cadAppLog('init initRenderer...');
    // 初始化渲染器
    RendererMgr.initRenderer(ctx2, '3d', () => {
      ctx2.viewer.requestRender();
      ctx2.viewer.sceneSetup.updatePixelBuffer();
      updateThumbnail();
    });
    RendererMgr.initRenderer(ctx2, '2d', () => {
      ctx2.viewer.requestRender();
      ctx2.viewer.sceneSetup.updatePixelBuffer();
      updateThumbnail();
    });
    RendererMgr.initRenderer(ctx2, 'assembly', () => {
      ctx2.viewer.requestRender();
      ctx2.viewer.sceneSetup.updatePixelBuffer();
      updateThumbnail();
    });
    // 初始化Picker
    const requestRenderFunction = () => {
      ctx2.viewer.requestRender();
    };

    PickerMgr.initPicker(ctx2, '3d', requestRenderFunction);
    PickerMgr.initPicker(ctx2, '2d', requestRenderFunction);
    PickerMgr.initPicker(ctx2, 'assembly', requestRenderFunction);

    // 设置snaper
    Snaper.init(ctx2);

    cadAppLog('[debug]init referenceManager...', ctx2.viewer.sceneSetup);
    referenceManager.init(ctx2.viewer.sceneSetup);
    cadAppLog('[debug]init referenceRenderManager...');
    referenceRenderManager.init();
    measureRenderManager.init(docType === 'product');
  };

  return (
    <>
      {inited && <MarkRenderer />}
      <DocumentContext.Provider
        value={{
          workingObjectDxid,
          docTree,
          documentInfo,
        }}
      >
        <EnvironmentContext.Provider value={environmentId}>
          <UserSettingProvider>
            <ConfigProvider theme={AntDesignTheme}>
              <Playground
                ctx2={ctx2}
                style={{
                  position: 'relative',
                  height: 'calc(100vh - ' + deltaHeight + 'px)',
                }}
                showLoading={showLoading}
                loadingProgress={loadingProgress}
                showConnectMessage={showConnectMessage}
                cmdPreCheckers={cmdPreCheckers}
                CommandBarComponent={CommandBar}
                CommandUpBarComponent={CommandUpBar}
              >
                {inited && FeatureManager.isFeatureEnabled('unit') && (
                  <UnitSwitch />
                )}
                {inited && <CSS2DObjectReactRenderer />}
              </Playground>
            </ConfigProvider>
          </UserSettingProvider>
        </EnvironmentContext.Provider>
      </DocumentContext.Provider>

      <CommonReloadModal
        open={openReload}
        loading={false}
        reloadPage={() => {
          window.location.reload();
        }}
        backHome={() => {
          window.location.replace('/projects');
        }}
        message='加载可视数据失败，请返回主页或刷新页面。'
      />
      <ToastContainer></ToastContainer>

      <ErrorNotifier />
    </>
  );
}
